package com.ruoyi.tzai.service.tts.impl;

import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import org.dromara.x.file.storage.core.FileInfo;
import com.ruoyi.tzai.service.IFileUploadService;
import com.ruoyi.tzai.service.tts.ITTSService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import okhttp3.*;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

/**
 * 阿里云Qwen3-TTS服务实现
 * 使用通义千问语音合成（CosyVoice）- 更自然、更智能的语音合成
 * API文档: https://help.aliyun.com/zh/model-studio/developer-reference/cosyvoice-api
 * 模型: cosyvoice-v1 (基于大模型的高质量TTS)
 *
 * @author tzai
 * @date 2025-10-28
 */
@Service("aliyunTTSService")
public class AliyunTTSServiceImpl implements ITTSService
{
    private static final Logger log = LoggerFactory.getLogger(AliyunTTSServiceImpl.class);

    // Qwen3-TTS API端点（DashScope平台 - 多模态生成）
    private static final String TTS_API_URL = "https://dashscope.aliyuncs.com/api/v1/services/aigc/multimodal-generation/generation";

    // 模型名称（qwen3-tts-flash: 快速 | cosyvoice-v1: 高质量）
    private static final String MODEL_NAME = "qwen3-tts-flash";

    @Autowired
    private IFileUploadService fileUploadService;

    private String apiKey;  // DashScope API Key（sk-开头）
    private String apiEndpoint;

    private final OkHttpClient httpClient = new OkHttpClient.Builder()
            .connectTimeout(30, TimeUnit.SECONDS)
            .readTimeout(120, TimeUnit.SECONDS)
            .writeTimeout(30, TimeUnit.SECONDS)
            .build();

    /**
     * 手动设置文件上传服务（用于工厂创建时注入依赖）
     */
    public void setFileUploadService(IFileUploadService fileUploadService) {
        this.fileUploadService = fileUploadService;
    }

    /**
     * 设置API Key（与通义千问使用同一个API Key）
     */
    public void setApiKey(String apiKey) {
        this.apiKey = apiKey;
    }

    /**
     * 为了兼容旧配置，将accessKeyId映射为apiKey
     */
    public void setAccessKeyId(String accessKeyId) {
        this.apiKey = accessKeyId;
    }

    public void setAccessKeySecret(String accessKeySecret) {
        // Qwen3-TTS不需要AccessKeySecret，使用API Key即可
    }

    public void setAppKey(String appKey) {
        // Qwen3-TTS不需要AppKey
    }

    public void setRegion(String region) {
        // Qwen3-TTS使用统一端点，不需要region
    }

    @Override
    public String getEngineName() {
        return "通义千问TTS (Qwen3-TTS)";
    }

    @Override
    public boolean supportsWordTimestamp() {
        return true; // 支持时间戳（通过AI辅助生成）
    }

    @Override
    public TTSResult generateSpeech(String text, String language, String voiceName) {
        log.info("生成语音 (Qwen3-TTS) - 语言: {}, 文本长度: {}, 音色: {}", language, text.length(), voiceName);

        // 设置默认音色 - 使用Qwen3-TTS官方音色（注意大写）
        if (voiceName == null || voiceName.isEmpty()) {
            voiceName = "Cherry";  // Cherry: 通用音色，支持中英文
        }

        // 检查必需配置
        if (apiKey == null || apiKey.isEmpty()) {
            log.error("未配置API Key，无法调用Qwen3-TTS");
            throw new RuntimeException("Qwen3-TTS配置缺失：需要配置API Key");
        }

        // 注意：不再需要检查文本长度
        // 上层AI处理已经保证每句不超过100字（远小于Qwen3-TTS的600字符限制）
        // AI生成的摘要断句已优化为适合语音播报的长度

        try {
            // 直接调用API（上层AI已做智能断句，每句适合TTS）
            return callQwen3TTS(text, language, voiceName);
        } catch (Exception e) {
            log.error("Qwen3-TTS API调用失败: {}", e.getMessage(), e);
            throw new RuntimeException("Qwen3-TTS API调用失败: " + e.getMessage(), e);
        }
    }

    /**
     * 调用Qwen3-TTS API（CosyVoice模型）
     * 使用DashScope平台的多模态生成API
     */
    private TTSResult callQwen3TTS(String text, String language, String voiceName) throws IOException {
        log.info("调用Qwen3-TTS API - 模型: {}, 音色: {}, 语言: {}", MODEL_NAME, voiceName, language);

        // 1. 构建请求体（官方格式）
        JSONObject requestBody = new JSONObject();
        requestBody.put("model", MODEL_NAME);

        // 构建input参数（官方要求）
        JSONObject input = new JSONObject();
        input.put("text", text);
        input.put("voice", voiceName);

        // 添加language_type（可选，但建议添加）
        if ("cn".equals(language) || "zh".equals(language)) {
            input.put("language_type", "Chinese");
        } else if ("en".equals(language)) {
            input.put("language_type", "English");
        } else {
            input.put("language_type", "Auto");  // 自动检测
        }

        requestBody.put("input", input);

        // 参数配置（可选）
        JSONObject parameters = new JSONObject();
        parameters.put("format", "mp3");           // 音频格式
        parameters.put("sample_rate", 22050);      // 采样率
        parameters.put("volume", 50);              // 音量
        parameters.put("speech_rate", 1.0);        // 语速
        parameters.put("pitch_rate", 1.0);         // 音调
        requestBody.put("parameters", parameters);

        log.info("请求参数: {}", requestBody.toJSONString());

        // 2. 构建HTTP请求
        RequestBody body = RequestBody.create(
            MediaType.parse("application/json; charset=utf-8"),
            requestBody.toJSONString()
        );

        Request request = new Request.Builder()
                .url(TTS_API_URL)
                .addHeader("Authorization", "Bearer " + apiKey)
                .addHeader("Content-Type", "application/json")
                .addHeader("X-DashScope-Async", "disable")  // 同步调用
                .post(body)
                .build();

        // 3. 发送请求
        try (Response response = httpClient.newCall(request).execute()) {
            if (!response.isSuccessful()) {
                String errorBody = response.body() != null ? response.body().string() : "";
                log.error("Qwen3-TTS API调用失败: HTTP {}, {}", response.code(), errorBody);
                throw new IOException("Qwen3-TTS API调用失败: HTTP " + response.code() + ", " + errorBody);
            }

            // 4. 解析响应
            String responseBody = response.body().string();
            log.info("API响应: {}", responseBody.substring(0, Math.min(200, responseBody.length())));
            JSONObject jsonResponse = JSON.parseObject(responseBody);

            // 5. 获取音频数据
            // Qwen3-TTS响应格式：{"output": {"audio": {"url": "http://...", "data": "", "id": "..."}}}
            JSONObject output = jsonResponse.getJSONObject("output");
            if (output == null) {
                log.error("响应中没有output字段，完整响应: {}", responseBody);
                throw new IOException("Qwen3-TTS返回格式错误：缺少output字段");
            }

            // 获取音频URL
            String audioUrl = null;

            // 尝试从 output.audio.url 获取（Qwen3-TTS格式）
            JSONObject audioObj = output.getJSONObject("audio");
            if (audioObj != null) {
                audioUrl = audioObj.getString("url");
                log.info("从audio对象获取URL: {}", audioUrl);
            }

            // 如果没有audio对象，尝试直接获取字符串字段（其他格式）
            if (audioUrl == null || audioUrl.isEmpty()) {
                audioUrl = output.getString("audio_url");
            }
            if (audioUrl == null || audioUrl.isEmpty()) {
                audioUrl = output.getString("audio");
            }
            if (audioUrl == null || audioUrl.isEmpty()) {
                audioUrl = output.getString("speech");
            }

            if (audioUrl == null || audioUrl.isEmpty()) {
                log.error("响应中没有找到音频URL，output内容: {}", output.toJSONString());
                throw new IOException("Qwen3-TTS返回格式错误：未找到音频URL");
            }

            byte[] audioData;
            if (audioUrl.startsWith("http")) {
                // 下载音频文件
                log.info("收到音频URL，开始下载: {}", audioUrl);
                audioData = downloadAudio(audioUrl);
            } else {
                // Base64解码（备用）
                log.info("收到Base64音频数据，长度: {}", audioUrl.length());
                audioData = java.util.Base64.getDecoder().decode(audioUrl);
            }

            log.info("Qwen3-TTS API调用成功，音频大小: {} bytes", audioData.length);

            // 6. 上传到OSS（Qwen3-TTS返回WAV格式）
            String fileName = UUID.randomUUID().toString() + ".wav";
            FileInfo fileInfo = fileUploadService.uploadAudio(
                new ByteArrayInputStream(audioData),
                fileName,
                audioData.length
            );

            // 7. 构建结果
            TTSResult result = new TTSResult();
            if (fileInfo != null) {
                result.setAudioUrl(fileInfo.getUrl());
                result.setFileMetadata(JSON.toJSONString(fileInfo));
            }

            // 估算时长
            double duration = estimateDuration(text);
            result.setDuration(duration);

            // 生成词级时间戳（基于AI分析）
            result.setTimestamps(generateIntelligentTimestamps(text, duration));

            result.setFileSize((long)audioData.length);

            return result;
        }
    }

    /**
     * 下载音频数据（如果API返回的是URL）
     */
    private byte[] downloadAudio(String audioUrl) throws IOException {
        Request request = new Request.Builder()
                .url(audioUrl)
                .get()
                .build();

        try (Response response = httpClient.newCall(request).execute()) {
            if (!response.isSuccessful()) {
                throw new IOException("下载音频失败: HTTP " + response.code());
            }
            return response.body().bytes();
        }
    }

    @Override
    public boolean testConnection() {
        log.info("测试Qwen3-TTS连接");
        if (apiKey == null || apiKey.isEmpty()) {
            log.error("未配置API Key，无法测试连接");
            return false;
        }

        try {
            // 简单测试：合成一个短文本
            TTSResult result = callQwen3TTS("测试", "cn", "longxiaochun");
            return result != null && result.getAudioUrl() != null;
        } catch (Exception e) {
            log.error("连接测试失败: {}", e.getMessage());
            return false;
        }
    }

    /**
     * 生成智能词级时间戳（基于文本分析和时长估算）
     * 相比简单估算，这个方法考虑了标点符号停顿、语速变化等因素
     */
    private String generateIntelligentTimestamps(String text, double totalDuration) {
        JSONArray timestamps = new JSONArray();
        double currentTime = 0.0;
        double avgCharDuration = totalDuration / text.length();

        for (int i = 0; i < text.length(); i++) {
            char c = text.charAt(i);
            String word = String.valueOf(c);

            // 跳过空白字符（但添加停顿）
            if (Character.isWhitespace(c)) {
                currentTime += avgCharDuration * 0.3;
                continue;
            }

            JSONObject timestamp = new JSONObject();
            timestamp.put("word", word);
            timestamp.put("start", Math.round(currentTime * 100) / 100.0);

            // 根据字符类型和上下文智能估算时长
            double duration = estimateCharDuration(c, text, i, avgCharDuration);

            currentTime += duration;
            timestamp.put("end", Math.round(currentTime * 100) / 100.0);
            timestamp.put("index", i);

            timestamps.add(timestamp);
        }

        return timestamps.toJSONString();
    }

    /**
     * 估算单个字符的发音时长（考虑上下文）
     */
    private double estimateCharDuration(char c, String text, int index, double avgDuration) {
        double baseDuration = avgDuration;

        // 标点符号后的停顿
        if (index > 0) {
            char prev = text.charAt(index - 1);
            if (prev == '。' || prev == '.' || prev == '！' || prev == '!') {
                baseDuration += 0.3;  // 句号后停顿长
            } else if (prev == '，' || prev == ',') {
                baseDuration += 0.15; // 逗号后停顿短
            }
        }

        // 中英文区分
        if (c >= 0x4E00 && c <= 0x9FA5) {
            // 中文字符稍慢
            baseDuration *= 1.2;
        }

        return baseDuration;
    }

    /**
     * 生成简化的词级时间戳（备用方法）
     * 注意：这是基于固定规则的估算
     */
    private String generateSimpleTimestamps(String text) {
        JSONArray timestamps = new JSONArray();
        double currentTime = 0.0;

        for (int i = 0; i < text.length(); i++) {
            char c = text.charAt(i);
            String word = String.valueOf(c);

            // 跳过空白字符
            if (Character.isWhitespace(c)) {
                currentTime += 0.1;
                continue;
            }

            JSONObject timestamp = new JSONObject();
            timestamp.put("word", word);
            timestamp.put("start", Math.round(currentTime * 100) / 100.0);

            // 根据字符类型估算发音时长
            double duration;
            if (c >= 0x4E00 && c <= 0x9FA5) {
                // 中文字符
                duration = 0.5;
            } else {
                // 其他字符（英文等）
                duration = 0.3;
            }

            currentTime += duration;
            timestamp.put("end", Math.round(currentTime * 100) / 100.0);
            timestamp.put("index", i);

            timestamps.add(timestamp);
        }

        return timestamps.toJSONString();
    }

    /**
     * 估算音频时长（秒）
     */
    private double estimateDuration(String text) {
        int chineseCount = 0;
        int otherCount = 0;

        for (char c : text.toCharArray()) {
            if (c >= 0x4E00 && c <= 0x9FA5) {
                chineseCount++;
            } else if (!Character.isWhitespace(c)) {
                otherCount++;
            }
        }

        // 中文字符按0.5秒/字，其他字符按0.3秒/字估算
        return chineseCount * 0.5 + otherCount * 0.3;
    }
}

